#include"Socket.hpp"

#include<unistd.h>
#include<iostream>
#include<sys/types.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<vector>
#include<unordered_map>
#include"Channel.hpp"




class Poller
{
private:
#define REVENTS_SIZE 1024 
    int _epfd;      //epoll模型的操作句柄
    std::unordered_map<int,Channel*> _channels;  //保存管理的套接字以及对应的Channel
    struct epoll_event _revents[REVENTS_SIZE];  //用于从epoll模型中获取就绪的文件描述符及其就绪事件
private:
    //判断该文件描述符是否在epoll模型中
    bool IsInPoller(Channel* channel) const
    {
        auto it = _channels.find(channel->Fd());
        if(it == _channels.end()) return false;
        return true;
    }
    void Update(Channel* channel,int op)
    {
        struct epoll_event ev;
        ev.data.fd = channel->Fd();
        ev.events = channel->Events();
        int ret = epoll_ctl(_epfd,op,channel->Fd(),&ev);
        if(ret == -1) 
        {
            ERROR_LOG("epoll_ctl failed");
            abort();
        }
    }
public:             //提供的接口
    void UpdateEvents(Channel* channel)
    {
        if(IsInPoller(channel))
        {
            Update(channel,EPOLL_CTL_MOD);  //交给子函数去更新事件
            return ;
        }
        _channels.insert(std::make_pair(channel->Fd(),channel)); //添加管理
        Update(channel,EPOLL_CTL_ADD);
    }
    void Remove(Channel*channel)
    {
        //可以先判断一下有没有
        auto it = _channels.find(channel->Fd());
        if(it != _channels.end()) 
        {
            _channels.erase(it);
            Update(channel,EPOLL_CTL_DEL);
        }
    }
    size_t Poll(std::vector<Channel*> actives)  //返回给EventLoop模型
    {
        int cnt = epoll_wait(_epfd,_revents,REVENTS_SIZE,-1); //阻塞等待
        if(cnt<0) 
        {
            if(errno == EINTR) return cnt; //信号打断
            ERROR_LOG("epoll_wait failed");
            abort();
        }
        for(int i=0;i<cnt;++i)
        {
            //设置Channel对象中的就绪事件
            auto it = _channels.find(_revents[i].data.fd);
            if(it == _channels.end()) 
            {
                ERROR_LOG("find channel failed");
                abort();
            }
            it->second->SetRevents(_revents[i].events);  //设置Channel事件
            actives.push_back(it->second);              //在EventLoop中只需要调用Channel的HandlerEvents进行数据处理就行了
        }
        return cnt;
    }
};



